# -*- coding: utf-8 -*-
"""
@Time ： 2023/9/17 19:04
@Auth ： y.h
@File ：load_excel_properties.py
@IDE ：PyCharm
@Motto：The sooner you start to code, the longer the program will take. —— Roy Carlson
"""
from typing import List, Any

# TODO 配置读取的列() 与 指定列的数据类型() 同时配置会包指针越界错误,有空研究一下
class 读取Excel表单的配置():
    # 忽略 非 ASCII 字符 警告
    # 框架配置
    __primary_key_row = None
    __index_row = None
    __title_line = None

    # pandas 配置
    __io = None
    __sheet_name = 0
    __header = 0
    __names = None
    __index_col = None
    __usecols = None

    __dtype = None
    __engine = None
    __converters = None
    __true_values = None
    __false_values = None
    __skiprows = None
    __nrows = None
    __na_values = None
    __keep_default_na = True
    __verbose = False
    __parse_dates = False
    __date_parser = None
    __thousands = None
    __comment = None
    __skipfooter = 0
    __convert_float = True
    __mangle_dupe_cols = True

    # getter and setter
    def get_primary_key_row(self):
        return self.__primary_key_row

    def 配置主键列(self,row_id:int,is_increment = False):
        """

        :param row_id:
        :param is_increment: 是否自增
        :return:
        """
        if is_increment:
            self.__primary_key_row = "increment"
            return self
        else:
            if type(row_id) != type(int):

                row_id = 1

            row_id -= 1
            if row_id < 0:
                print(f"配置主键列的参数错误,需要大于0,当前值为{row_id + 1}")
                exit(True)
            else:
                self.__primary_key_row = row_id
                return self


    def get_sheet_name(self):
        """
        如果使用者已经用到这个方法了,还需要看中文方法解释,多少有点不合适
        :return:
        """
        return self.__sheet_name

    def 配置表单名(self, 表单名):
        """
        支持链式调用,即:读取Excel表单的配置().配置表单名(str).配置行标签(1)...
        :param 表单名: 接收需要读取的表单名,参数类型为:整型(表单索引),字符串(表单名),列表(可以是索引的集合,表名的集合,也可以是混合索引与表名,但不建议,如果为None,则全部读取)
        :return:表单配置对象
        """
        self.__sheet_name = 表单名
        return self

    def get_header(self):
        return self.__header

    def 配置字段所在行(self, 列序号):
        """
        最终体现在数据实体生成的参数
        :param header: 表示用第几行作为表头，默认0，即默认第一行为表头,参数类型为:整型(指定某一行)指定行之前的数据不读取,列表([4,5,6]设置4,5,6行为表头,4行之前的数据不读取,参数为None则不使用表头)
        :return:
        """
        self.__header = 列序号
        return self

    def get_names(self):
        return self.__names

    def 自定义全部字段名(self, 自定义字段数组):
        """
        最终体现在数据实体生成的参数
        :param 自定义字段数组: 表示自定义表头,接收一个列表,如["数据1","数据2",...]必须确保和数据列表长度相同,否则会报指针越界错误
        :return:
        """
        self.__names = 自定义字段数组
        return self

    def get_index_col(self):
        return self.__index_col

    def 配置索引(self, 列序号):
        """

        :param 列序号: 表示用第几列为数据索引,接收列序号,默认(0)为第一列
        :return:
        """
        self.__index_col = 列序号
        return self

    def get_usecols(self):
        return self.__usecols

    def 配置读取的列(self, 需要读取的列):
        """

        :param 需要读取的列: 参数类型为:整型(指定列),字符串(如:"C:E")读取Excel列号为C到E的数据,列表(如[2,5,9]:读取第2,5,9列的数据),参数为None则全读
        :return:
        """
        self.__usecols = 需要读取的列
        return self

    def get_dtype(self):
        return self.__dtype

    def 指定列的数据类型(self, 数据类型字典: dict):
        """
        不建议用,如果需要可以以字符串的形式读进来,再进行类型强制转换
        :param 数据类型字典:如{1:"uint64",5:str} 则将第一列指定为"长整型"类型,将第5列指定为字符串类型
        :return:
        """
        self.__dtype = 数据类型字典
        return self

    def get_engine(self):
        return self.__engine

    def 指定引擎解析Excel(self, 引擎: int):
        """

        :param 引擎:参数为1时引擎为"xlrd",2为"openpyxl",3为"odf",乱填为默认引擎
        :return:
        """
        try:
            engine = int(引擎)

            if type(engine) == int:
                if engine == 1:
                    self.__engine = "xlrd"
                elif engine == 2:
                    self.__engine = "openpyxl"
                elif engine == 3:
                    self.__engine = "odf"
                else:
                    self.__engine = None
            else:
                self.__engine = None
        except Exception:
            self.__engine = None
        finally:
            return self

    def get_converters(self):
        return self.__converters

    def 指定列进行指定函数的处理(self, 配置字典: dict):
        """
        对指定列的数据进行指定函数的处理，传入参数为列名与函数组成的字典。key 可以是列名或者列的序号，values是函数，可以def函数或者直接lambda都行。
        :param 配置字典:如:{1:sum(),2:lambda element: element + 10},对第2列执行方法sum(),对第三列执行所有元素加10
        :return:
        """
        self.__converters = 配置字典
        return self

    def get_true_values(self):
        return self.__true_values

    def 将指定的文本转为True(self, 指定文本: list):
        """

        :param 指定文本:如参数为["男"] 则将参数为男的文本转为True
        :return:
        """
        self.__true_values = 指定文本
        return self

    def get_false_values(self):
        return self.__false_values

    def 将指定的文本转为False(self, 指定文本: list):
        """

        :param 指定文本: 如参数为["男"] 则将参数为男的文本转为False
        :return:
        """
        self.__false_values = 指定文本
        return self

    def get_skiprows(self):
        return self.__skiprows

    def 跳过指定的行(self, 指定的行: list):
        """
        这个方法不适合封装为传参为lambda表达式的形式,有被注入攻击的风险,如需要,可调用pandas.read_excel(skiprows = lambda: x: x % 2 == 0)跳过偶数行,这种形式调用
        :param 指定的行: 如参数为[2,6,11],则跳过第2,6,11行,参数为[3],则跳过前三行
        :return:
        """
        self.__skiprows = 指定的行
        return self

    def get_nrows(self):
        return self.__nrows

    def 配置读取前几行(self, 读取前几行: int):
        self.__nrows = 读取前几行
        return self

    def get_na_values(self):
        return self.__na_values

    def 指定某些列的某些值为NaN(self, 指定的列和值):
        self.__na_values = 指定的列和值
        return self

    def get_keep_default_na(self):
        return self.__keep_default_na

    def 是否自动识别空值并导入(self, 是或否: str):
        if 是或否 == "否":
            self.__keep_default_na = False
        return self
